#pragma once

#include"atom.h"

typedef const ivec4* Coupling;
typedef const ivec4* CouplingType;

#ifdef GRAPHENETOOLS_EXPORTS
#ifndef GRAPHENETOOLS_DLL_API
#define GRAPHENETOOLS_DLL_API __declspec(dllexport)
#endif
#else
#ifndef GRAPHENETOOLS_DLL_API
#define GRAPHENETOOLS_DLL_API __declspec(dllimport)
#endif
#endif

class GRAPHENETOOLS_DLL_API Bond
{
	const Atom* orig;				// This container holds the pointer to the origin atom.
	const Atom* dest;				// This container holds the pointer to the destination atom.
	Coupling coupling;		// This container holds the coupling as a pointer to the coupling vector.
	CouplingType couplingtype;	// This container holds the coupling type as a pointer to the first coupling vector of the same set.

public:
	// The defined coupling bonds:

	static const Coupling invalid;				// The invalid coupling (a nullptr).
	static const ivec4 SameAtom[1];				// The carbon coupling to it self.
	static const ivec4 NearestNeighbours[6];	// The graphene nearest neighbour bonds. Only from A(B) atoms to B(A) atoms in the same (i,j)-plane.
	static const ivec4 DimerNeighbours[2];		// The bi-layer graphene dimer bonds across layers. Only from B atoms to A atoms in the bottom and top (i,j)-planes, respectfully.
	static const ivec4 DimerNextNeighbours[6];	// The bi-layer graphene nearest neighbour dimer-to-non-dimer bonds across layers. Only from A atoms to A atoms or B to B in the bottom and top (i,j)-planes.
	static const ivec4 NonDimerNeighbours[6];	// The bi-layer graphene nearest neighbour non-dimer bonds across layers. Only from A or B atoms to A and B atoms in the bottom or top (i,j)-planes, respectfully.
	
	Bond();		// The default bond constructor returns an invalid bond.
	Bond(const Atom &a,const Atom &n, const ivec4 &periodicvector = ivec4(arma::fill::zeros)); // Overload constructor returns a bond depending of the Atoms a and n. Check validity using isValid().
	
	const Atom* getOrigin() const;			// This getter function returns a pointer to the origin Atom.
	const Atom* getDestination() const;		// This getter function returns a pointer to the destination Atom.
	vec3 getXyz() const;					// This getter function returns the bond vector in xyz coordinates.
	vec3 xyz() const;						// This returns the bond vector in xyz coordinates.

	Coupling getCoupling() const;			// This getter function returns the coupling vector.
	CouplingType getCouplingType() const;	// This getter function returns the coupling type.
	bool isValid() const;					// This convenience function test if the coupling is valid. Same as getCouplingType() != Bond::invalid.
	
	typedef std::vector<Bond> list; // Convenience typedef.
	typedef std::vector<list> table; // Convenience typedef.
	
	bool isBetweenSameAtom() const;
	bool isBetweenInplaneNN() const;
	bool isBetweenDimerNN() const;
	bool isBetweenNonDimerNN() const;
	bool isBetweenDimerNonDimerNN() const;
	
protected:
	// This function returns a bond between two atoms. If the bond is not defined an invalid bond is returned.
	void retrieveBond(const Atom &a, const Atom &n, const ivec4 &periodicvector);
	// This convinience function only looks for NearestNeighbour bonds.
	void retrieveSameSublatticeBond(const Atom &a, const Atom &n, const ivec4 &periodicvector);
	// This convinience function only looks for NearestNeighbour bonds.
	void retrieveDifferentSublatticeBond(const Atom &a, const Atom &n, const ivec4 &periodicvector);
	// This convinience function only looks for DimerNextNeighbours and NonDimerNeighbours bonds.
	void retrieveStackedSameSublatticeBond(const Atom &a, const Atom &n, const ivec4 &periodicvector);
	// This convinience function only looks for DimerNeighbours and NonDimerNeighbours bonds.
	void retrieveStackedDifferentSublatticeBond(const Atom &a, const Atom &n, const ivec4 &periodicvector);

	static bool equal(const ivec4 &v1, const ivec4 &v2); // This convenience function behaves like an equality operator (==).
};